{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Objects and Classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A class is a type of object. In Python we create classes using the `class` keyword."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now this class doesn't do much, but it is an object of type `type` (which is itself an object)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "type"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(Person)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "type"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(type)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Classes have \"built-in\" attributes, even though we did not specifically add any to the class ourselves.\n",
    "\n",
    "For example, they have a name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Person'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Person.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "They are also callables, and calling a class results in the creation and return of a new **instance** of that class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = Person()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the type of the object is the class used to build that object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Person"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These instances also have \"built_in\" properties, which we will cover throughout this course.\n",
    "\n",
    "For example, they have a `__class__` property that tells us which class was used to create the instance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Person"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p.__class__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see that returns the class object used to instantiate `p`.\n",
    "\n",
    "In fact:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(p) is p.__class__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also use `isinstance` to test if an object is an instance of a particular class - now this gets a bit more complicated when we use inheritance, but right now we're not, so it's quite straightforward:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(p, Person)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(p, str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can even use `isinstance` with our class, since we know it's type is `type`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(Person, type)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`type` is like the most generic kind of **class** object - we'll come back to this when discussing meta programming.\n",
    "\n",
    "We really need inheritance to understand how this works, but every class **is** a `type` object (it inherits all the properties of `type`).\n",
    "\n",
    "For now let's just see what functionality `type` has:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on class type in module builtins:\n",
      "\n",
      "class type(object)\n",
      " |  type(object_or_name, bases, dict)\n",
      " |  type(object) -> the object's type\n",
      " |  type(name, bases, dict) -> a new type\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __call__(self, /, *args, **kwargs)\n",
      " |      Call self as a function.\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __dir__(...)\n",
      " |      __dir__() -> list\n",
      " |      specialized __dir__ implementation for types\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __init__(self, /, *args, **kwargs)\n",
      " |      Initialize self.  See help(type(self)) for accurate signature.\n",
      " |  \n",
      " |  __instancecheck__(...)\n",
      " |      __instancecheck__() -> bool\n",
      " |      check if an object is an instance\n",
      " |  \n",
      " |  __new__(*args, **kwargs)\n",
      " |      Create and return a new object.  See help(type) for accurate signature.\n",
      " |  \n",
      " |  __prepare__(...)\n",
      " |      __prepare__() -> dict\n",
      " |      used to create the namespace for the class statement\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  __sizeof__(...)\n",
      " |      __sizeof__() -> int\n",
      " |      return memory consumption of the type object\n",
      " |  \n",
      " |  __subclasscheck__(...)\n",
      " |      __subclasscheck__() -> bool\n",
      " |      check if a class is a subclass\n",
      " |  \n",
      " |  __subclasses__(...)\n",
      " |      __subclasses__() -> list of immediate subclasses\n",
      " |  \n",
      " |  mro(...)\n",
      " |      mro() -> list\n",
      " |      return a type's method resolution order\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors defined here:\n",
      " |  \n",
      " |  __abstractmethods__\n",
      " |  \n",
      " |  __dict__\n",
      " |  \n",
      " |  __text_signature__\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __base__ = <class 'object'>\n",
      " |      The most base type\n",
      " |  \n",
      " |  __bases__ = (<class 'object'>,)\n",
      " |  \n",
      " |  __basicsize__ = 864\n",
      " |  \n",
      " |  __dictoffset__ = 264\n",
      " |  \n",
      " |  __flags__ = 2148291584\n",
      " |  \n",
      " |  __itemsize__ = 40\n",
      " |  \n",
      " |  __mro__ = (<class 'type'>, <class 'object'>)\n",
      " |  \n",
      " |  __weakrefoffset__ = 368\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(type)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see it has a `__call__` method (that's how our class becomes callable), and a bunch of other attributes and methods that we'll see throughout this course.\n",
    "\n",
    "Our class objects also have these properties, because they inherit from the `type` object."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And in fact, `type` is an instance of itself - that's kind of weird, and not the case for our own classes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(type, type)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(Person, Person)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
